﻿using Scope8.Data;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI;
using Windows.UI.Input;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
using Windows.UI.Xaml.Navigation;
using Windows.UI.Xaml.Shapes;

// Die Elementvorlage "Standardseite" ist unter http://go.microsoft.com/fwlink/?LinkId=234237 dokumentiert.

namespace Scope8
{
    /// <summary>
    /// Die Viewerseite zum anzeigen des gewünschten Slides. slideEntrys die hier angezeigt werden,
    /// müssen von der Klasse Slide geerbt haben und deren Methoden implementiert haben.
    /// </summary>
    /// 


    public sealed partial class SlideViewer : Scope8.Common.LayoutAwarePage
    {
        /// <summary>
        /// slideObjekt für den Zugriff auf Methoden etc.
        /// </summary>
        private Slide slideEntry;

        /// <summary>
        /// Debugausgabe an/aus
        /// </summary>
        private Boolean DEBUG = false;

        //TOUCH VARIABLEN
        /// <summary>
        /// Anzahl der aktiven Kontakte
        /// </summary>
        private uint numActiveContacts;

        /// <summary>
        /// Möglichkeiten des Gerätes
        /// </summary>
        //private Windows.Devices.Input.TouchCapabilities touchCapabilities = new Windows.Devices.Input.TouchCapabilities();

        /// <summary>
        /// Erlaubte Kontakt
        /// </summary>
        const uint SUPPORTEDCONTACTS = 2;

        /// <summary>
        /// Dict mit dem Punkt über die aktuellen Touchkontakte
        /// </summary>
        Dictionary<uint, Point?> contacts;

        /// <summary>
        /// Anfangs und Endpunkt einer Manipulation
        /// </summary>
        private Point PosStart;
        private Point PosEnd;



        public SlideViewer()
        {
            this.InitializeComponent();

            //Pointerevents definieren
            numActiveContacts = 0; //kein Kontakt mit Display
            contacts = new Dictionary<uint, Point?>((int)SUPPORTEDCONTACTS);
            ViewSlide.RenderTransform = new CompositeTransform();
            ViewSlide.ManipulationDelta += new ManipulationDeltaEventHandler(ViewSlideManipulationDelta);
            ViewSlide.ManipulationStarted += new ManipulationStartedEventHandler(ViewSlideManipulationStarted);
            ViewSlide.ManipulationInertiaStarting += new ManipulationInertiaStartingEventHandler(ViewSlideManipulationInertiaStarting);
            ViewSlide.ManipulationCompleted += new ManipulationCompletedEventHandler(ViewSlideManipulationCompleted);
            scrollViewer.PointerReleased += new PointerEventHandler(ViewSlide_PointerReleased);
            scrollViewer.PointerPressed += new PointerEventHandler(ViewSlide_PointerPressed);
        }


        /// <summary>
        /// Füllt die Seite mit Inhalt auf, der bei der Navigation übergeben wird. Gespeicherte Zustände werden ebenfalls
        /// bereitgestellt, wenn eine Seite aus einer vorherigen Sitzung neu erstellt wird.
        /// </summary>
        /// <param name="navigationParameter">Der Parameterwert, der an
        /// <see cref="Frame.Navigate(Type, Object)"/> übergeben wurde, als diese Seite ursprünglich angefordert wurde.
        /// </param>
        /// <param name="pageState">Ein Wörterbuch des Zustands, der von dieser Seite während einer früheren Sitzung
        /// beibehalten wurde. Beim ersten Aufrufen einer Seite ist dieser Wert NULL.</param>
        protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
        {
            // Zulassen, dass das anfänglich anzuzeigende Element vom gespeicherten Seitenzustand überschrieben wird
            if (pageState != null && pageState.ContainsKey("SelectedItem"))
            {
                navigationParameter = pageState["SelectedItem"];
            }

            var item = SlideDataSource.GetItem((String)navigationParameter);
            slideEntry = item.AnSlideEntry;

            //Übersichtsbild Laden, die Auflösung des Übersichtsfensters wird dynamisch herrangezogen.
            OverViewImage.Source = new BitmapImage(new Uri(@slideEntry.InitializeSlide(OverViewCanvas.Width, OverViewCanvas.Height)));

            //Bild in Viewer laden
            //Aktuelle Auflösung abfragen abzüglich des Topmenüs
            double viewfildwidth = (Window.Current.Bounds.Width);
            double viewfildheight = (Window.Current.Bounds.Height - 140);
            ViewSlide.Source = new BitmapImage(new Uri(@slideEntry.InitializeSlide(viewfildwidth,viewfildheight)));

            if(DEBUG) debugText.Text = slideEntry.getManipulatedUrl();

            //wird nicht durchgeführt da zu diesem Zeitpunkt noch keine Werte abgerufen werden können
            //OverViewBackgroundRect.Height = OverViewImage.Height + 2;
            //OverViewBackgroundRect.Width = OverViewImage.Width + 2;
        }


        /// <summary>
        /// Behält den dieser Seite zugeordneten Zustand bei, wenn die Anwendung angehalten oder
        /// die Seite im Navigationscache verworfen wird. Die Werte müssen den Serialisierungsanforderungen
        /// von <see cref="SuspensionManager.SessionState"/> entsprechen.
        /// </summary>
        /// <param name="pageState">Ein leeres Wörterbuch, das mit dem serialisierbaren Zustand aufgefüllt wird.</param>
        protected override void SaveState(Dictionary<String, Object> pageState)
        {
        }


        /// <summary>
        /// Wird benötigt um in zukünftigen Versionen einen Pointer Lost Event abzufangen und dadurch ein Auslaufen des Slides zu generieren 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ViewSlideManipulationInertiaStarting(object sender, ManipulationInertiaStartingRoutedEventArgs e)
        {
            if(DEBUG) pointerDetails.Text = ("Pointer lost");
            //Auslaufen lassen
            //e.TranslationBehavior.DesiredDeceleration = 100 * 96.0 / (1000.0 * 1000.0);
            //e.ExpansionBehavior.DesiredDeceleration = 0.1 * 96 / 1000.0 * 1000.0;
            //e.RotationBehavior.DesiredDeceleration = 720 / (1000.0 * 1000.0);
            e.Handled = true;
        }


        /// <summary>
        /// Eine Manipulation wurde abgeschlossen und das Slide wird mit den neuen Parametern berechnet und neu geladen
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ViewSlideManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e)
        {

            CompositeTransform transform = ViewSlide.RenderTransform as CompositeTransform;

            //Bildverschiebungen abarbeiten(absolutpixel werden genutzt)
            slideEntry.pan(PosStart, PosEnd);

            //Bildzoom, %-Wert der vergößerung wird genutzt
            slideEntry.zoom(transform.ScaleX, transform.ScaleY);

            //Position zurücksetzten da das neue Slide am Ausgangspunkt geladen werden muss und nicht verzerrt dargestellt werden darf
            transform.TranslateX = 0;
            transform.TranslateY = 0;
            transform.ScaleX = 1.0;
            transform.ScaleY = 1.0;

            refreshMainSlideSource();

            if (DEBUG) pointerDetails.Text = "Manipulation completed";
        }

        /// <summary>
        /// Läd das Slide mit dem Parametern des Objektes für den Viewer neu
        /// </summary>
        private void refreshMainSlideSource()
        {
            debugText.Text = slideEntry.getManipulatedUrl();
            ViewSlide.Source = new BitmapImage(new Uri(@slideEntry.getManipulatedUrl()));
        }

        /// <summary>
        /// Es wurde eine Manipulation des Slides gestartet (event)
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ViewSlideManipulationStarted(object sender, ManipulationStartedRoutedEventArgs e)
        {
            if (DEBUG) pointerDetails.Text = "Manipulation started";

            e.Handled = true; 
        }

        /// <summary>
        /// Methode zur manipulation des angezeigten Slides - noch kein neu laden nur transformation!
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ViewSlideManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
        {
            if (DEBUG) pointerDetails.Text = "Manipulation des des Slides(angezeigtes)";
            UIElement element = sender as UIElement;
            CompositeTransform transform = element.RenderTransform as CompositeTransform;
            if (transform != null)
            {
                //ZOOM zentriert ausführen
                transform.CenterX = ViewSlide.ActualWidth / 2; 
                transform.CenterY = ViewSlide.ActualHeight / 2;

                //transform.Rotation += e.Delta.Rotation * 180 / Math.PI; //für Rotation

                //Wichtig erst PAN dann ZOOM sonst zerschießt es einen den Zoom!
                //PAN bearbeiten
                if (slideEntry.xTransPossible(e.Delta.Translation.X)) transform.TranslateX += e.Delta.Translation.X;
                if (slideEntry.yTransPossible(e.Delta.Translation.Y)) transform.TranslateY += e.Delta.Translation.Y;
                //Zoom bearbeiten
                if (slideEntry.zoomPossible(e.Delta.Scale))
                {
                    transform.ScaleX *= e.Delta.Scale;
                    transform.ScaleY *= e.Delta.Scale;
                }
            }
            e.Handled = true;
        }


        /// <summary>
        /// Bewegungen des Pointers erfassen und bei Bedarf Debuginformationen ausgeben
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ViewSlide_PointerMoved(object sender, PointerRoutedEventArgs e)
        {
            Windows.UI.Input.PointerPoint ptrPt = e.GetCurrentPoint(ViewSlide);
            uint ptrId = ptrPt.PointerId;
            if (ptrPt.Properties.PointerUpdateKind != Windows.UI.Input.PointerUpdateKind.Other)
            {
                if(DEBUG) pointerDetails.Text = "Moved: \n" + ptrPt.Properties.PointerUpdateKind.ToString();
            }
            e.Handled = true;
        }


        /// <summary>
        /// Event welches generiert wird wenn ein neuer Pointerkontakt entsteht. Die Startpositionen dieses Kontaktes wird gesichert um dann später für den Zoom genutzt zu werden
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ViewSlide_PointerPressed(object sender, PointerRoutedEventArgs e)
        {
            if (numActiveContacts >= SUPPORTEDCONTACTS)
            {
                // Es sind nicht mehr gleichzeitige Kontakte erlaubt!
                return;
            }

            Windows.UI.Input.PointerPoint ptrPt = e.GetCurrentPoint(scrollViewer);
            contacts[ptrPt.PointerId] = ptrPt.Position;
            
            //Aktive Kontakte herrauf setzen
            ++numActiveContacts;

            

            //zur volles Auflösung des Bildschirms passende XY Koordinate bestimmen
            GeneralTransform gt = ViewSlide.TransformToVisual(pageRoot);
            Point screenPoint;
            screenPoint = gt.TransformPoint(new Point(ptrPt.Position.X, ptrPt.Position.Y));

            //Merken der Startposition
            PosStart.X = screenPoint.X;
            PosStart.Y = screenPoint.Y;

            if (DEBUG) showPointerDebugDetails(sender, e, "Pressed");

            e.Handled = true;
        }


        /// <summary>
        /// Event welches generiert wird wenn ein Pointerkontakt beendet wird. Die Endposition dieses Kontaktes wird gesichert um dann später für den Zoom genutzt zu werden
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void ViewSlide_PointerReleased(object sender, PointerRoutedEventArgs e)
        {
            uint ptrId = e.GetCurrentPoint(sender as FrameworkElement).PointerId;
            Windows.UI.Input.PointerPoint ptrPt = e.GetCurrentPoint(scrollViewer);

            //Wenn dieser Kontakt in der Liste der vorher bearbeiteten Kontakte ist kann er nun gelöscht werden (fehlerkennungen umgehen)
            if (contacts.ContainsKey(ptrId))
            {
                contacts[ptrId] = null;
                contacts.Remove(ptrId);
                --numActiveContacts;

                //zur volles Auflösung des Bildschirms passende XY Koordinate bestimmen
                GeneralTransform gt = ViewSlide.TransformToVisual(pageRoot);
                Point screenPoint;
                screenPoint = gt.TransformPoint(new Point(ptrPt.Position.X, ptrPt.Position.Y));
                PosEnd.X = screenPoint.X;
                PosEnd.Y = screenPoint.Y;

                if(DEBUG) showPointerDebugDetails(sender, e, "Released");
            }
            e.Handled = true;
        }


        /// <summary>
        /// Zeigt im oberen auf dem GUI Debuginformationen zu Pointer Aktionen an.
        /// </summary>
        /// <param name="sender">Eventhandler der das Event bearbeitet hat</param>
        /// <param name="e"></param>
        /// <param name="status">Selbstdefinierter String der mit ausgegeben wird</param>
        private void showPointerDebugDetails(object sender, PointerRoutedEventArgs e, String status)
        {
            Windows.UI.Input.PointerPoint ptrPt = e.GetCurrentPoint(scrollViewer);

            if (ptrPt.Properties.PointerUpdateKind != Windows.UI.Input.PointerUpdateKind.Other)
            {
                //Update debugText
                if (DEBUG)
                {
                    String details = status;

                    switch (ptrPt.PointerDevice.PointerDeviceType)
                    {
                        case Windows.Devices.Input.PointerDeviceType.Mouse:
                            details += "\nPointer type: mouse";
                            break;
                        case Windows.Devices.Input.PointerDeviceType.Pen:
                            details += "\nPointer type: pen";
                            if (ptrPt.IsInContact)
                            {
                            }
                            break;
                        case Windows.Devices.Input.PointerDeviceType.Touch:
                            details += "\nPointer type: touch";
                            break;
                        default:
                            details += "\nPointer type: n/a";
                            break;
                    }
                    GeneralTransform gt = ViewSlide.TransformToVisual(pageRoot);
                    Point screenPoint;

                    screenPoint = gt.TransformPoint(new Point(ptrPt.Position.X, ptrPt.Position.Y));
                    details += "\nPointer Id: " + ptrPt.PointerId.ToString() +
                       "\nPointer location (parent): " + ptrPt.Position.X + ", " + ptrPt.Position.Y +
                        "\nPointer location (screen): " + screenPoint.X + ", " + screenPoint.Y +
                        "\nNumOffContacts: " + numActiveContacts; ;
                    pointerDetails.Text = details;
                }
            }
        }

        /// <summary>
        /// Fallbackzoom +
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Button_Click_Plus(object sender, RoutedEventArgs e)
        {
            if (slideEntry.zoomPossible(1.2F))
            {
                slideEntry.zoom(1.2, 1.2);
                refreshMainSlideSource();
            }

        }


        /// <summary>
        /// Fallbackzoom -
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Button_Click_Minus(object sender, RoutedEventArgs e)
        {
            if (slideEntry.zoomPossible(0.8F))
            {
                slideEntry.zoom(0.8, 0.8);
                refreshMainSlideSource();
            }           
        }
    }
}
